package com.hex.ds.hdrs.watch.exec;

import com.hex.ds.hdrs.agent.service.AgentManager;
import com.hex.ds.hdrs.agent.service.AgentSession;
import com.hex.ds.hdrs.center.service.CenterManager;
import com.hex.ds.hdrs.common.constant.Const;
import com.hex.ds.hdrs.common.utils.HdrsDateUtil;
import com.hex.ds.hdrs.common.utils.ListUtil;
import com.hex.ds.hdrs.log.service.LogService;
import com.hex.ds.hdrs.watch.service.ScheduledService;
import com.hex.ds.hdrs.watch.service.WatchService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 监控线程
 */
@Slf4j
@EnableScheduling
@Component
public class WatchExec {

    @Autowired
    private AgentManager agentManager;
    @Autowired
    private CenterManager centerManager;
    @Autowired
    private ScheduledService scheduledService;
    @Autowired
    private WatchService watchService;
    @Autowired
    private LogService logService;

    @Value("${agent.heartOverTs}")
    private int heartOverTs;

    @Value("${scheduled.watch.cleanDay}")
    private String cleanDay;

    /**
     * 监控agent 心跳
     */
    @Scheduled(fixedDelayString = "${scheduled.watch.agentSessionFixedDelay}", initialDelayString = "${scheduled.watch.agentSessionInitialDelay}")
    private void monitorAgentSession() {
        if (!centerManager.isCanDeal()) {
            log.warn("【monitorAgentSession】中心不可处理");
            return;
        }
        try {
            List<AgentSession> validAgentSessionList = agentManager.getAgentSessionList();
            if (ListUtil.isEmpty(validAgentSessionList)) {
                return;
            }
            for (AgentSession agentSession : validAgentSessionList) {
                if (HdrsDateUtil.getMillis(new Date()) - agentSession.getLstHeartBeatTs() > heartOverTs) {
                    log.info("AGENT心跳异常【agentCode: {},curTs: {}】", agentSession.getAgentCode(), HdrsDateUtil.getCurDate());
                    // DB停止
                    Map map = new HashMap(16);
                    map.put("agentHaStatus", Const.AGENT_HA_STATUS_STOP);
                    map.put("agentCode", agentSession.getAgentCode());
                    scheduledService.updateAgentStatus(map);
                    // 内存
                    agentManager.stopAgent(agentSession.getAgentCode(), "agent心跳超时，agent下线!");
                    scheduledService.agentOfflineNotice(agentSession.getAgentConf());
                }
            }
        } catch (Exception e) {
            log.error("监控agent 心跳【WatchExec.monitorAgentSession】【异常】======》{}", e.getMessage(), e);
        }
    }

    /**
     * 监控 正在执行任务是否超时
     */
    @Scheduled(fixedDelayString = "${scheduled.watch.taskExecFixedDelay}", initialDelayString = "${scheduled.watch.taskExecInitialDelay}")
    public void monitorTaskExec() {
        try {
            scheduledService.killAgentTask();
        } catch (Exception e) {
            log.error("监控 正在执行任务是否超时【WatchExec.monitorDispThread】【异常】======》{}", e.getMessage(), e);
        }
    }

    /**
     * 监控  定时更新失败任务
     */
    @Scheduled(fixedDelayString = "${scheduled.watch.taskFailAnewFixedDelay}", initialDelayString = "${scheduled.watch.taskFailAnewInitialDelay}")
    public void failTaskAnewDeal() {
        log.debug("监控 定时更新失败任务【WatchExec.failTaskAnewDeal】开始======》");
        try {
            //更新失败的作业任务，且出错次数在出错范围之内
            scheduledService.updateFailTask();
            //更新未超时执行中的任务为待执行，执行中队列不存在
            //scheduledService.updateRunningTask();

            scheduledService.monitorSessionTask();
            //更新所有未执行成功的作业状态为执行成功，且该作业下没有未成功的任务
            //更新所有执行失败但是任务没有失败的作业
            scheduledService.updateExceptionJob();
        } catch (Exception e) {
            log.error("监控 定时更新失败任务【WatchExec.failTaskAnewDeal】【异常】======》{}", e.getMessage(), e);
        }
    }

    /**
     * 监控  内存信息
     */
    //@Scheduled(fixedDelayString = "${scheduled.watch.memoryFixedDelay}", initialDelayString = "${scheduled.watch.memoryInitialDelay}")
    public void monitorMemory() {
        log.info("监控  内存信息【WatchExec.monitorMemory】开始======》");
        try {
            scheduledService.toRunMemoryDeal();
            scheduledService.runningMemoryDeal();
        } catch (Exception e) {
            log.error("监控  内存信息【WatchExec.monitorMemory】【异常】======》{}", e.getMessage(), e);
        }
    }

    /**
     * 中心预警
     */
    @Scheduled(fixedDelayString = "${scheduled.watch.centerWarnFixedDelay}", initialDelayString = "${scheduled.watch.centerWarnInitialDelay}")
    public void centerWarnNotice() {
        log.info("中心预警【WatchExec.centerWarnNotice】-线程号: {}", Thread.currentThread().getName());
        try {
            if (centerManager.isMaster()) {
                String nowTime = HdrsDateUtil.format(new Date(), "HH:mm");
                //1、Agent 资源预警
                this.scheduledService.agentResourceWarnNotic();
                //2、Agent 上下线汇总
                this.scheduledService.agentOnAndOffline(nowTime);
                //3、内存队列大小预警
                this.scheduledService.queueNumMonitor();
                //4、DB任务实例数量预警
                this.scheduledService.dbTaskNumMonitor();
            }
        } catch (Exception e) {
            log.error("中心预警【WatchExec.centerWarnNotice】【异常】======》{}", e.getMessage(), e);
        }
    }

    /**
     * 应用预警
     */
    @Scheduled(fixedDelayString = "${scheduled.watch.appWarnFixedDelay}", initialDelayString = "${scheduled.watch.appWarnInitialDelay}")
    public void appWarnNotice() {
        log.info("应用预警【WatchExec.appWarnNotice】-线程号: {}", Thread.currentThread().getName());
        try {
            if (centerManager.isMaster()) {
                String nowTime = HdrsDateUtil.format(new Date(), "HH:mm");
                //1.定期发送任务执行结果通知
                this.scheduledService.taskReportTimingSummary(nowTime);
                //2.指定任务指定时间未完成预警
                this.scheduledService.appointTimeTaskNoFinish(nowTime);
                //3.今日任务失败数过多预警
                this.scheduledService.taskFailOut(nowTime);
                //4.指定时间内任务未完成数量预警
                this.scheduledService.taskNoFinishOut(nowTime);
                //5.任务异常超时未处理预警
                this.scheduledService.failTaskHandleTimeOut(nowTime);
                //6.指定任务超时未完成预警
                this.scheduledService.taskRunningTimeOut();
            }
        } catch (Exception e) {
            log.error("应用预警【WatchExec.appWarnNotice】【异常】======》{}", e.getMessage(), e);
        }
    }

    /**
     * 监控Running中和agent中以及DB中任务存在状况
     * 进行相应的补偿措施
     */
    //@Scheduled(fixedDelayString = "${scheduled.watch.runningAndAgentFixedDelay}", initialDelayString = "${scheduled.watch.runningAndAgentInitialDelay}")
    private void monitorRunningAndAgent() {
        log.info("开始监控Running中和agent中以及DB中任务存在状况");
        try {
            scheduledService.monitorRunningAndAgent();
        } catch (Exception e) {
            log.error("监控Running中和agent中以及DB中任务存在状况出错{}", e.getMessage(), e);
        }
    }

    /**
     * @Method: againSessionResAndCurr <br>
     * @Param: [] <br>
     * @Return: void <br>
     * @Description：重新计算资源数和并发数<br>
     * @Author： gc.zuo<br>
     * @Date： 2021/6/29 15:20 <br>
     * @Version： V1.0<br>
     */
    @Scheduled(cron = "${scheduled.watch.cron}")
    private void againSessionResAndCurr() {
        log.info("重新计算资源数和并发数【SessionExec.againSessionResAndCurr】开始====");
        watchService.againSessionResAndCurr();
    }

    /**
     * @Method: cleanTask <br>
     * @Param: [] <br>
     * @Return: void <br>
     * @Description：定时清理历史任务<br>
     * @Author： gc.zuo<br>
     * @Date： 2022/8/16 11:00 <br>
     * @Version： V1.0<br>
     */
    @Scheduled(cron = "${scheduled.watch.cleanTask}")
    private void cleanTask() {
        log.info("清理历史任务【cleanTask】开始====");
        watchService.cleanTask(cleanDay);
    }

}
